package com.lhs.service;

import com.lhs.config.redis.RedisKeyConstants;
import com.lhs.config.redis.ZiDeRedisUtil;
import com.lhs.entity.blog.blog_post;
import com.lhs.mapper.blog_post_0Mapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HotBlogService {

    @Autowired
    private ZiDeRedisUtil ziDeRedisUtil;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private blog_post_0Mapper blogPostMapper;

    /**
     * 检查并更新热点文章列表
     */
    public void checkAndUpdateHotBlog(Long postId) {
        // 获取文章当前点赞数和浏览数
        String likeCountKey = String.format(RedisKeyConstants.BLOG_LIKE_COUNT, postId);
        String viewCountKey = String.format(RedisKeyConstants.BLOG_VIEW_COUNT, postId);

        Long likeCount = ziDeRedisUtil.getCacheObject(likeCountKey);
        Long viewCount = ziDeRedisUtil.getCacheObject(viewCountKey);

        if (likeCount == null) likeCount = 0L;
        if (viewCount == null) viewCount = 0L;

        // 定义热点文章标准：点赞数超过1000或浏览数超过10000
        if (likeCount > 1000 || viewCount > 10000) {
            // 使用分布式锁，防止多个线程同时更新热点列表
            RLock lock = redissonClient.getLock(RedisKeyConstants.LOCK_BLOG_HOT);

            try {
                if (lock.tryLock(3, 5, TimeUnit.SECONDS)) {
                    // 将文章加入热点文章ZSet，以点赞数和浏览数的加权得分作为排序依据
                    double score = likeCount * 0.7 + viewCount * 0.3; // 权重可调整
                    ziDeRedisUtil.addToZSet(RedisKeyConstants.HOT_BLOG, postId.toString(), score);

                    // 只保留前100个热点文章
                    ziDeRedisUtil.zRemoveRange(RedisKeyConstants.HOT_BLOG, 0, -101);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("更新热点文章列表被中断", e);
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }

    /**
     * 获取热点文章列表
     */
    public List<Long> getHotBlogs(int limit) {
        // 从ZSet中获取得分最高的文章ID
        Set<String> hotBlogIds = ziDeRedisUtil.zReverseRange(RedisKeyConstants.HOT_BLOG, 0, limit - 1);
        return hotBlogIds.stream()
                .map(Long::valueOf)
                .collect(Collectors.toList());
    }

    /**
     * 处理热点文章缓存 - 防止缓存击穿[9](@ref)
     */
    public blog_post getHotBlogWithCacheProtection(Long postId) {
        String cacheKey = "blog:hot:info:" + postId;

        // 1. 先尝试从缓存获取
        blog_post blogPost = ziDeRedisUtil.getCacheObject(cacheKey);
        if (blogPost != null) {
            return blogPost;
        }

        // 2. 使用互斥锁重建缓存[9](@ref)
        String lockKey = "lock:blog:hot:rebuild:" + postId;
        RLock lock = redissonClient.getLock(lockKey);

        try {
            if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
                // 双重检查，可能其他线程已经重建了缓存
                blogPost = ziDeRedisUtil.getCacheObject(cacheKey);
                if (blogPost != null) {
                    return blogPost;
                }

                // 从数据库查询
                blogPost = blogPostMapper.selectBlogPostById(postId);
                if (blogPost != null) {
                    // 设置随机过期时间，防止大量热点key同时过期[9](@ref)
                    int expireTime = 3600 + new Random().nextInt(300); // 1小时 + 随机5分钟
                    ziDeRedisUtil.setCacheObject(cacheKey, blogPost, expireTime, TimeUnit.SECONDS);
                }

                return blogPost;
            } else {
                // 获取锁失败，返回默认值或等待
                log.warn("获取热点文章缓存重建锁失败，postId: {}", postId);
                return null;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("获取热点文章被中断", e);
            return null;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}